import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * 
 * @author pfister
 * 
 */

public class ClientCanvas {

	private static final boolean DRAW_ON_DRAG = true;

	private static final int MAX_LOG = 200;

	ClientSocket client;
	private String host = "127.0.0.1";
	private int port = 8261;

	private JFrame frmPaintclient;
	
	private JPanel panelPaint = new JPanel() {
		@Override
		protected void paintComponent(Graphics graphics) {
			super.paintComponent(graphics);
			drawShapes(graphics);
		}
	};

	enum Shape {
		rectangle, circle
	};

	enum MyColor {
		black, red, green
	};

	private boolean thick;

	private BasicStroke thickStroke = new BasicStroke(5.0f);
	private BasicStroke thinStroke = new BasicStroke(.5f);
	private Graphics2D brush;

	private Shape currentShape = Shape.rectangle;
	private MyColor currentColor = MyColor.black;

	private int dragX, dragY;
	private int pressedX, pressedY;
	private boolean draggin;

	private List<String> commands = new ArrayList<String>();

	private JTextArea txtLog;
	private JTextArea textLog1;
	private JTextArea textLog2;
	private JTextArea textLog3;

	private JButton btnThick;

	private void unsetBrush() {
		brush.dispose();
		brush = null;
	}

	private void setUpBrush(Graphics graphics) {
		Graphics2D g2d_ = null;
		if (graphics == null) {
			g2d_ = (Graphics2D) panelPaint.getGraphics();
			brush = (Graphics2D) g2d_;
		} else{
			brush = (Graphics2D) graphics;
			g2d_ = brush;
		}
		if (g2d_ != null) {
			g2d_.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
			switch (currentColor) {
			case black:
				g2d_.setPaint(Color.BLACK);
				break;
			case red:
				g2d_.setPaint(Color.RED);
				break;
			case green:
				g2d_.setPaint(Color.GREEN);
				break;
			default:
				g2d_.setPaint(Color.BLACK);
			}
			g2d_.setStroke(thick ? thickStroke : thinStroke);
		}
	}

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					ClientCanvas window = new ClientCanvas();
					window.frmPaintclient.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	private void showError(String host, int port, String message) {
		JOptionPane.showMessageDialog(frmPaintclient, message, "host=" + host
				+ " port=" + port, JOptionPane.ERROR_MESSAGE);
	}

	public ClientCanvas() {
		initialize();
		setUpBrush(null);
		currentShape = Shape.rectangle;
		client = new ClientSocket(host, port);
		try {
			client.init();
			ClientThread ct = new ClientThread(client.getSocket());
			ct.start();
		} catch (Exception e) {
			showError(host, port, e.getMessage());
			System.exit(0);
		}
	}

	private void clog(String mesg) {
		System.out.println(mesg);
	}

	public void trace(int out, String m) {
		JTextArea target = null;
		switch (out) {
		case 1:
			target = txtLog;
			break;
		case 2:
			target = textLog1;
			break;
		case 3:
			target = textLog2;
			break;
		case 4:
			target = textLog3;
			break;
		default:
			target = textLog1;
			break;
		}
		
		if (target.getText().length()>MAX_LOG)
			target.setText("");
		target.setText(m + "\r\n" + target.getText());
	}

	private void drawShapes(Graphics graphics) {
		setUpBrush(graphics);
		for (String cmd : commands) {
			String[] parsed = cmd.split(" ");
			if (parsed[0].equals("rectangle")) {
				brush.drawRect(Integer.parseInt(parsed[1]),
						Integer.parseInt(parsed[2]),
						Integer.parseInt(parsed[3]),
						Integer.parseInt(parsed[4]));
			} else if (parsed[0].equals("circle")) {
				brush.drawOval(Integer.parseInt(parsed[1]),
						Integer.parseInt(parsed[2]),
						Integer.parseInt(parsed[3]),
						Integer.parseInt(parsed[3]));
			}
		}
		graphics.dispose();
	}

	public void dragged(MouseEvent evt) {
		if (draggin == false)
			return;
		int x = evt.getX();
		int y = evt.getY();
		if (DRAW_ON_DRAG){
			setUpBrush(null);
			brush.drawLine(dragX, dragY, x, y);
			unsetBrush();
		}
		dragX = x;
		dragY = y;
		clog("dragged " + x + "." + y);
	}

	public void pressed(MouseEvent evt) {
		int x = evt.getX();
		int y = evt.getY();
		trace(2, "pressed " + x + "." + y);
		dragX = x;
		dragY = y;
		pressedX = x;
		pressedY = y;
		draggin = true;
	}

	public void released(MouseEvent evt) {
		int x = evt.getX();
		int y = evt.getY();
		int width = x - pressedX;
		int height = y - pressedY;
		trace(2, "released " + x + "." + y);
		if (draggin == false)
			return;
		draggin = false;
		switch (currentShape) {
		case rectangle:
			String cmd = String.format("rectangle %d %d %d %d", pressedX,
					pressedY, width, height);
			trace(1, cmd);
			commands.add(cmd);
			client.send(cmd);
			panelPaint.repaint();
			break;
		case circle:
			int radius = Math.min(width, height);
			cmd = String.format("circle %d %d %d", pressedX, pressedY, radius);
			trace(1, cmd);
			commands.add(cmd);
			client.send(cmd);
			panelPaint.repaint();
			break;
		default:
			break;
		}
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {

		frmPaintclient = new JFrame();
		frmPaintclient.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent winEvt) {
				clog("closing");
				System.exit(0);
			}
		});

		frmPaintclient.setTitle("PaintClient");
		frmPaintclient.setBounds(100, 100, 450, 430);
		frmPaintclient.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frmPaintclient.getContentPane()
				.setLayout(
						new BoxLayout(frmPaintclient.getContentPane(),
								BoxLayout.X_AXIS));
		JSplitPane splitPaneFrame = new JSplitPane();
		splitPaneFrame.setResizeWeight(0.8);
		splitPaneFrame.setOrientation(JSplitPane.VERTICAL_SPLIT);
		frmPaintclient.getContentPane().add(splitPaneFrame);
		JSplitPane splitPanePaint = new JSplitPane();
		splitPanePaint.setResizeWeight(0.2);
		splitPaneFrame.setLeftComponent(splitPanePaint);
		panelPaint.setBackground(new Color(224, 255, 255));
		panelPaint.addMouseMotionListener(new MouseMotionAdapter() {
			@Override
			public void mouseDragged(MouseEvent evt) {
				dragged(evt);
			}

		});
		panelPaint.addMouseListener(new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent evt) {
				pressed(evt);
			}

			@Override
			public void mouseReleased(MouseEvent evt) {
				released(evt);
			}
		});

		splitPanePaint.setRightComponent(panelPaint);

		JPanel panelPalette = new JPanel();
		splitPanePaint.setLeftComponent(panelPalette);
		GridBagLayout gbl_panelPalette = new GridBagLayout();
		gbl_panelPalette.columnWidths = new int[] { 22 };
		gbl_panelPalette.rowHeights = new int[] { 23, 0, 0, 0, 0 };
		gbl_panelPalette.columnWeights = new double[] { 1.0 };
		gbl_panelPalette.rowWeights = new double[] { 0.0, 0.0, 0.0, 1.0,
				Double.MIN_VALUE };
		panelPalette.setLayout(gbl_panelPalette);

		JButton btnRectangle = new JButton("Rectangle");
		btnRectangle.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				currentShape = Shape.rectangle;
			}
		});

		btnThick = new JButton("Thick");
		btnThick.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (!thick){
				  thick = true;
				  btnThick.setText("Thin");
				} else{
					  thick = false;
					  btnThick.setText("Thick");				
				}
			}
		});
		GridBagConstraints gbc_btnThick = new GridBagConstraints();
		gbc_btnThick.fill = GridBagConstraints.HORIZONTAL;
		gbc_btnThick.insets = new Insets(0, 0, 5, 0);
		gbc_btnThick.gridx = 0;
		gbc_btnThick.gridy = 0;
		panelPalette.add(btnThick, gbc_btnThick);
		GridBagConstraints gbc_btnRectangle = new GridBagConstraints();
		gbc_btnRectangle.fill = GridBagConstraints.HORIZONTAL;
		gbc_btnRectangle.anchor = GridBagConstraints.NORTH;
		gbc_btnRectangle.insets = new Insets(0, 0, 5, 0);
		gbc_btnRectangle.gridx = 0;
		gbc_btnRectangle.gridy = 1;
		panelPalette.add(btnRectangle, gbc_btnRectangle);

		JButton btnCircle = new JButton("Circle");
		btnCircle.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				currentShape = Shape.circle;
			}
		});
		GridBagConstraints gbc_btnCircle = new GridBagConstraints();
		gbc_btnCircle.fill = GridBagConstraints.HORIZONTAL;
		gbc_btnCircle.insets = new Insets(0, 0, 5, 0);
		gbc_btnCircle.anchor = GridBagConstraints.NORTH;
		gbc_btnCircle.gridx = 0;
		gbc_btnCircle.gridy = 2;
		panelPalette.add(btnCircle, gbc_btnCircle);

		txtLog = new JTextArea();
		GridBagConstraints gbc_txtLog = new GridBagConstraints();
		gbc_txtLog.fill = GridBagConstraints.BOTH;
		gbc_txtLog.gridx = 0;
		gbc_txtLog.gridy = 3;
		panelPalette.add(txtLog, gbc_txtLog);

		JPanel panelLog = new JPanel();
		splitPaneFrame.setRightComponent(panelLog);
		GridBagLayout gbl_panelLog = new GridBagLayout();
		gbl_panelLog.columnWidths = new int[] { 0, 0 };
		gbl_panelLog.rowHeights = new int[] { 30, 30, 0 };
		gbl_panelLog.columnWeights = new double[] { 1.0, Double.MIN_VALUE };
		gbl_panelLog.rowWeights = new double[] { 1.0, 1.0, 1.0 };
		panelLog.setLayout(gbl_panelLog);

		textLog1 = new JTextArea();
		GridBagConstraints gbc_textLog1 = new GridBagConstraints();
		gbc_textLog1.insets = new Insets(0, 0, 5, 0);
		gbc_textLog1.fill = GridBagConstraints.BOTH;
		gbc_textLog1.gridx = 0;
		gbc_textLog1.gridy = 0;
		panelLog.add(textLog1, gbc_textLog1);

		textLog2 = new JTextArea();
		GridBagConstraints gbc_textLog2 = new GridBagConstraints();
		gbc_textLog2.insets = new Insets(0, 0, 5, 0);
		gbc_textLog2.fill = GridBagConstraints.BOTH;
		gbc_textLog2.gridx = 0;
		gbc_textLog2.gridy = 1;
		panelLog.add(textLog2, gbc_textLog2);

		textLog3 = new JTextArea();
		GridBagConstraints gbc_textLog3 = new GridBagConstraints();
		gbc_textLog3.fill = GridBagConstraints.BOTH;
		gbc_textLog3.gridx = 0;
		gbc_textLog3.gridy = 2;
		panelLog.add(textLog3, gbc_textLog3);
	}

	class ClientThread extends Thread {

		private Socket socket;
		private boolean stopSession = false;;

		public ClientThread(Socket socket) {
			this.socket = socket;
		}

		public void run() {
			BufferedReader reader;
			try {
				reader = new BufferedReader(new InputStreamReader(
						socket.getInputStream()));
				while (!stopSession) {
					String fromServer = reader.readLine();
					trace(3, fromServer);
					if (fromServer.startsWith("echo ")) {
						fromServer = fromServer.substring(5);
						commands.add(fromServer);
						panelPaint.repaint();
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

}
